package com.quectel.taskserver.job.warnhousehold;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.quectel.business.common.websocket.WebSocketComponent;
import com.quectel.business.common.websocket.send.SendMsgType;
import com.quectel.constant.core.PreWarnConstants;
import com.quectel.constant.core.eventrecord.PersonAccessRecordConstants;
import com.quectel.constant.core.household.HouseholdConstants;
import com.quectel.core.module.household.dto.HouseholdDto;
import com.quectel.core.module.household.service.HouseholdService;
import com.quectel.core.module.prewarning.dto.PreWarningConfigDto;
import com.quectel.core.module.prewarning.dto.PreWarningRecordDto;
import com.quectel.core.module.prewarning.service.PreWarningConfigService;
import com.quectel.core.module.prewarning.service.PreWarningRecordService;
import com.quectel.util.common.DateUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.kit.PageFetchKit;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.simple.job.SimpleJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yubobo
 * @email bobo.yu@quectel.com
 * @date 2022/1/10 下午2:17
 * 智能预警 人员久出未归 久归为出
 */
@Component
public class PreWarnHouseholdJob implements SimpleJob {

    private static final Logger LOGGER = LoggerFactory.getLogger(PreWarnHouseholdJob.class);

    @DubboReference
    private PreWarningConfigService preWarningConfigService;
    @DubboReference
    private PreWarningRecordService preWarningRecordService;
    @DubboReference
    private HouseholdService householdService;
    @Autowired
    private WebSocketComponent webSocketComponent;

    @Override
    public void execute(ShardingContext shardingContext) {
        checkWarning();
        cleanWarning();
    }

    private void checkWarning(){
        final Date currentDate = new Date();

        LOGGER.info("人员久出久入预警检测开始 时间 {}", DateUtils.format(currentDate, DateUtils.FormatType.COMMON));

        try {
            Map<String, Object> queryMap = new HashMap<>();
            queryMap.put(LambdaUtil.getFieldName(PreWarningConfigDto::getStatus), PreWarnConstants.AlarmStatusEnum.ENABLE.getStatus());
            List<PreWarningConfigDto> enableConfigList = preWarningConfigService.queryList(queryMap);

            Map<Long, List<PreWarningConfigDto>> configListGroupByVillage = enableConfigList.parallelStream().collect(Collectors.groupingBy(PreWarningConfigDto::getVillageId));
            for (Map.Entry<Long, List<PreWarningConfigDto>> longListEntry : configListGroupByVillage.entrySet()) {
                Long villageId = longListEntry.getKey();

                PreWarningConfigDto inNoOutConfig = null;
                PreWarningConfigDto outNoInConfig = null;
                for (PreWarningConfigDto preWarningConfigDto : longListEntry.getValue()) {
                    if (PreWarnConstants.AlarmTypeEnum.LONG_IN_NO_OUT.getType().equals(preWarningConfigDto.getPreWarnType())) {
                        inNoOutConfig = preWarningConfigDto;
                    } else if (PreWarnConstants.AlarmTypeEnum.LONG_OUT_NO_IN.getType().equals(preWarningConfigDto.getPreWarnType())) {
                        outNoInConfig = preWarningConfigDto;
                    }
                }

                List<HouseholdDto> villageHouseholdList = new ArrayList<>();
                if (Objects.nonNull(inNoOutConfig)) {
                    Map<String, Object> queryHouseholdMap = new HashMap<>();
                    queryHouseholdMap.put(LambdaUtil.getFieldName(HouseholdDto::getVillageId), villageId);
                    queryHouseholdMap.put(LambdaUtil.getFieldName(HouseholdConstants.HouseHoldExtraAttr::getInOrOut), PersonAccessRecordConstants.InOrOutEnum.IN.getInOrOut());
                    queryHouseholdMap.put(LambdaUtil.getFieldName(HouseholdConstants.HouseHoldExtraAttr::getEventTime), DateUtils.format(DateUtil.offsetHour(currentDate, -1 * inNoOutConfig.getRuleTime()), DateUtils.FormatType.COMMON));
                    villageHouseholdList.addAll(PageFetchKit.fetchAll(req -> householdService.queryList(req), queryHouseholdMap, 500));
                }
                if (Objects.nonNull(outNoInConfig)) {
                    Map<String, Object> queryHouseholdMap = new HashMap<>();
                    queryHouseholdMap.put(LambdaUtil.getFieldName(HouseholdDto::getVillageId), villageId);
                    queryHouseholdMap.put(LambdaUtil.getFieldName(HouseholdConstants.HouseHoldExtraAttr::getInOrOut), PersonAccessRecordConstants.InOrOutEnum.OUT.getInOrOut());
                    queryHouseholdMap.put(LambdaUtil.getFieldName(HouseholdConstants.HouseHoldExtraAttr::getEventTime), DateUtils.format(DateUtil.offsetHour(currentDate, -1 * outNoInConfig.getRuleTime()), DateUtils.FormatType.COMMON));
                    villageHouseholdList.addAll(PageFetchKit.fetchAll(req -> householdService.queryList(req), queryHouseholdMap, 500));
                }

                for (HouseholdDto householdDto : villageHouseholdList) {
                    if (Objects.isNull(householdDto.getExtraAttr())) {
                        // 不满足判断条件
                        continue;
                    }

                    try {
                        HouseholdConstants.HouseHoldExtraAttr preWarningExtTimeDto = JacksonUtils.parse(householdDto.getExtraAttr(), HouseholdConstants.HouseHoldExtraAttr.class);

                        if (PersonAccessRecordConstants.InOrOutEnum.IN.getInOrOut().equals(preWarningExtTimeDto.getInOrOut())) {
                            // 进入的状态，只用判断是否的久入预警，如果存在出预警，需要解除预警
                            PreWarningRecordDto oldWarningRecordDto = preWarningRecordService.selectOne(householdDto.getId(), PreWarnConstants.AlarmTypeEnum.LONG_OUT_NO_IN.getType());
                            if (Objects.nonNull(oldWarningRecordDto)) {
                                preWarningRecordService.clean(oldWarningRecordDto.getId());
                            }

                            if (Objects.isNull(inNoOutConfig)) {
                                // 入预警配置不存在，无法判断告警
                                continue;
                            }
                            PreWarningRecordDto inNoOutRecord = preWarningRecordService.selectOne(householdDto.getId(), PreWarnConstants.AlarmTypeEnum.LONG_IN_NO_OUT.getType());
                            if (Objects.nonNull(inNoOutRecord)
                                    && DateUtil.isSameTime(inNoOutRecord.getLastAccessTime(), preWarningExtTimeDto.getEventTime())) {
                                // 存在告警，并且同一时间事件，仍处于告警
                                PreWarningRecordDto updateObj = new PreWarningRecordDto();
                                updateObj.setId(inNoOutRecord.getId());
                                updateObj.setCheckTime(currentDate);
                                preWarningRecordService.updateById(updateObj);
                            } else {
                                if (Objects.nonNull(inNoOutRecord)) {
                                    preWarningRecordService.clean(inNoOutRecord.getId());
                                }
                                long between = DateUtil.between(preWarningExtTimeDto.getEventTime(), currentDate, DateUnit.HOUR, Boolean.TRUE);
                                if (inNoOutConfig.getRuleTime() <= between) {
                                    // 产生告警
                                    PreWarningRecordDto preWarningRecordDto = new PreWarningRecordDto();
                                    preWarningRecordDto.setTenantId(inNoOutConfig.getTenantId());
                                    preWarningRecordDto.setVillageId(inNoOutConfig.getVillageId());
                                    preWarningRecordDto.setObjId(householdDto.getId());
                                    preWarningRecordDto.setPersonName(householdDto.getName());
                                    preWarningRecordDto.setPreWarnType(PreWarnConstants.AlarmTypeEnum.LONG_IN_NO_OUT.getType());
                                    preWarningRecordDto.setLastAccessTime(preWarningExtTimeDto.getEventTime());
                                    preWarningRecordDto.setLastSnapImages(preWarningExtTimeDto.getSnapImages());
                                    preWarningRecordDto.setAlarmTime(currentDate);
                                    preWarningRecordDto.setCheckTime(currentDate);
                                    preWarningRecordDto.setConfigId(inNoOutConfig.getId());
                                    preWarningRecordDto.setConfigBak(JacksonUtils.toJsonString(inNoOutConfig));
                                    Long save = preWarningRecordService.save(preWarningRecordDto);
                                    if (save != null) {
                                        webSocketComponent.sendMsgToCmsApi(householdDto.getVillageId()
                                                , SendMsgType.PRE_WARN.getMsgTypeFlag()
                                                , SendMsgType.PRE_WARN.getTitle()
                                                , SendMsgType.PRE_WARN.getContentFunc().apply(new String[]{householdDto.getName()
                                                        , DateUtils.format(currentDate, DateUtils.FormatType.COMMON)
                                                        , inNoOutConfig.getName()})
                                                , null);
                                    }
                                }
                            }

                        } else if (PersonAccessRecordConstants.InOrOutEnum.OUT.getInOrOut().equals(preWarningExtTimeDto.getInOrOut())) {
                            // 出去的状态，只用判断是否为久出预警，如果存在入预警，需要解除预警
                            PreWarningRecordDto oldWarningRecordDto = preWarningRecordService.selectOne(householdDto.getId(), PreWarnConstants.AlarmTypeEnum.LONG_IN_NO_OUT.getType());
                            if (Objects.nonNull(oldWarningRecordDto)) {
                                preWarningRecordService.clean(oldWarningRecordDto.getId());
                            }

                            if (Objects.isNull(outNoInConfig)) {
                                // 入预警配置不存在，无法判断告警
                                continue;
                            }
                            PreWarningRecordDto outNoInRecord = preWarningRecordService.selectOne(householdDto.getId(), PreWarnConstants.AlarmTypeEnum.LONG_OUT_NO_IN.getType());
                            if (Objects.nonNull(outNoInRecord)
                                    && DateUtil.isSameTime(outNoInRecord.getLastAccessTime(), preWarningExtTimeDto.getEventTime())) {
                                // 存在告警，并且同一事件事件，仍处于告警
                                PreWarningRecordDto updateObj = new PreWarningRecordDto();
                                updateObj.setId(outNoInRecord.getId());
                                updateObj.setCheckTime(currentDate);
                                preWarningRecordService.updateById(updateObj);
                            } else {
                                if (Objects.nonNull(outNoInRecord)) {
                                    preWarningRecordService.clean(outNoInRecord.getId());
                                }
                                long between = DateUtil.between(preWarningExtTimeDto.getEventTime(), currentDate, DateUnit.HOUR, Boolean.TRUE);
                                if (outNoInConfig.getRuleTime() <= between) {
                                    // 产生告警
                                    PreWarningRecordDto preWarningRecordDto = new PreWarningRecordDto();
                                    preWarningRecordDto.setTenantId(outNoInConfig.getTenantId());
                                    preWarningRecordDto.setVillageId(outNoInConfig.getVillageId());
                                    preWarningRecordDto.setObjId(householdDto.getId());
                                    preWarningRecordDto.setPersonName(householdDto.getName());
                                    preWarningRecordDto.setPreWarnType(PreWarnConstants.AlarmTypeEnum.LONG_OUT_NO_IN.getType());
                                    preWarningRecordDto.setLastAccessTime(preWarningExtTimeDto.getEventTime());
                                    preWarningRecordDto.setLastSnapImages(preWarningExtTimeDto.getSnapImages());
                                    preWarningRecordDto.setAlarmTime(currentDate);
                                    preWarningRecordDto.setCheckTime(currentDate);
                                    preWarningRecordDto.setConfigId(outNoInConfig.getId());
                                    preWarningRecordDto.setConfigBak(JacksonUtils.toJsonString(outNoInConfig));
                                    Long save = preWarningRecordService.save(preWarningRecordDto);
                                    if (save != null) {
                                        webSocketComponent.sendMsgToCmsApi(householdDto.getVillageId()
                                                , SendMsgType.PRE_WARN.getMsgTypeFlag()
                                                , SendMsgType.PRE_WARN.getTitle()
                                                , SendMsgType.PRE_WARN.getContentFunc().apply(new String[]{householdDto.getName()
                                                        , DateUtils.format(currentDate, DateUtils.FormatType.COMMON)
                                                        , outNoInConfig.getName()})
                                                , null);
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        LOGGER.warn("住户进出状态备份扩展字段转换错误", e);
                    }
                }


            }
        } catch (Exception e) {
            LOGGER.error("人员久出久入预警检测异常", e);
        }

    }

    private void cleanWarning(){
        final Date currentDate = new Date();

        List<PreWarningRecordDto> preWarningRecordDtos = PageFetchKit.fetchAll(req -> preWarningRecordService.queryList(req), new HashMap<>(), 500);
        Map<Long, List<PreWarningRecordDto>> recordGroupByVillageMap = preWarningRecordDtos.parallelStream().collect(Collectors.groupingBy(PreWarningRecordDto::getVillageId));
        for (Map.Entry<Long, List<PreWarningRecordDto>> longListEntry : recordGroupByVillageMap.entrySet()) {
            PreWarningConfigDto inConfig = preWarningConfigService.selectOne(longListEntry.getKey(), PreWarnConstants.AlarmTypeEnum.LONG_IN_NO_OUT.getType());
            PreWarningConfigDto outConfig = preWarningConfigService.selectOne(longListEntry.getKey(), PreWarnConstants.AlarmTypeEnum.LONG_OUT_NO_IN.getType());

            Map<Byte, List<PreWarningRecordDto>> collect = longListEntry.getValue().parallelStream().collect(Collectors.groupingBy(PreWarningRecordDto::getPreWarnType));
            for (Map.Entry<Byte, List<PreWarningRecordDto>> byteListEntry : collect.entrySet()) {
                if (PreWarnConstants.AlarmTypeEnum.LONG_IN_NO_OUT.getType().equals(byteListEntry.getKey())) {

                    if (Objects.isNull(inConfig)) {
                        byteListEntry.getValue().parallelStream().map(PreWarningRecordDto::getId).forEach(preWarningRecordService::clean);
                    } else {
                        for (PreWarningRecordDto preWarningRecordDto : byteListEntry.getValue()) {
                            HouseholdDto householdDto = householdService.selectById(preWarningRecordDto.getObjId());
                            if (Objects.isNull(householdDto)) {
                                preWarningRecordService.clean(preWarningRecordDto.getId());
                                continue;
                            }

                            HouseholdConstants.HouseHoldExtraAttr preWarningExtTimeDto = JacksonUtils.parse(householdDto.getExtraAttr(), HouseholdConstants.HouseHoldExtraAttr.class);

                            if (Objects.isNull(preWarningExtTimeDto.getEventTime())
                                    || PersonAccessRecordConstants.InOrOutEnum.OUT.getInOrOut().equals(preWarningExtTimeDto.getInOrOut())
                                    || inConfig.getRuleTime() > DateUtil.between(preWarningExtTimeDto.getEventTime(), currentDate, DateUnit.HOUR, Boolean.TRUE)) {
                                preWarningRecordService.clean(preWarningRecordDto.getId());
                            }
                        }
                    }

                } else if (PreWarnConstants.AlarmTypeEnum.LONG_OUT_NO_IN.getType().equals(byteListEntry.getKey())) {

                    if (Objects.isNull(outConfig)) {
                        byteListEntry.getValue().parallelStream().map(PreWarningRecordDto::getId).forEach(preWarningRecordService::clean);
                    } else {
                        for (PreWarningRecordDto preWarningRecordDto : byteListEntry.getValue()) {
                            HouseholdDto householdDto = householdService.selectById(preWarningRecordDto.getObjId());
                            if (Objects.isNull(householdDto)) {
                                preWarningRecordService.clean(preWarningRecordDto.getId());
                                continue;
                            }

                            HouseholdConstants.HouseHoldExtraAttr preWarningExtTimeDto = JacksonUtils.parse(householdDto.getExtraAttr(), HouseholdConstants.HouseHoldExtraAttr.class);

                            if (Objects.isNull(preWarningExtTimeDto.getEventTime())
                                    || PersonAccessRecordConstants.InOrOutEnum.IN.getInOrOut().equals(preWarningExtTimeDto.getInOrOut())
                                    || outConfig.getRuleTime() > DateUtil.between(preWarningExtTimeDto.getEventTime(), currentDate, DateUnit.HOUR, Boolean.TRUE)) {
                                preWarningRecordService.clean(preWarningRecordDto.getId());
                            }
                        }
                    }

                }
            }

        }


    }
}